﻿Imports System.Collections.Generic
Imports System.Threading

Namespace DoFactory.GangOfFour.Singleton.RealWorld
    ''' <summary>
    ''' MainApp startup class for Real-World
    ''' Singleton Design Pattern.
    ''' </summary>
    Public Class MainApp
        ''' <summary>
        ''' Entry point into console application.
        ''' </summary>
        Public Shared Sub Main()
            Dim b1 As LoadBalancer = LoadBalancer.GetLoadBalancer()
            Dim b2 As LoadBalancer = LoadBalancer.GetLoadBalancer()
            Dim b3 As LoadBalancer = LoadBalancer.GetLoadBalancer()
            Dim b4 As LoadBalancer = LoadBalancer.GetLoadBalancer()

            ' Same instance?
            If b1 Is b2 AndAlso b2 Is b3 AndAlso b3 Is b4 Then
                Console.WriteLine("Same instance" & vbLf)
            End If

            ' Load balance 15 server requests
            Dim balancer As LoadBalancer = LoadBalancer.GetLoadBalancer()
            For i As Integer = 0 To 14
                Dim server As String = balancer.Server
                Console.WriteLine("Dispatch Request to: " & server)
            Next

        End Sub

    End Class

    ''' <summary>
    ''' The 'Singleton' class
    ''' </summary>
    Class LoadBalancer

        Private Shared _instance As LoadBalancer

        Private _servers As New List(Of String)()

        Private _random As New Random()

        ' Lock synchronization object
        Private Shared [syncLock] As New Object()
        ' Constructor (protected)
        Protected Sub New()
            ' List of available servers
            _servers.Add("ServerI")
            _servers.Add("ServerII")
            _servers.Add("ServerIII")
            _servers.Add("ServerIV")
            _servers.Add("ServerV")
        End Sub

        Public Shared Function GetLoadBalancer() As LoadBalancer
            ' Support multithreaded applications through
            ' 'Double checked locking' pattern which (once
            ' the instance exists) avoids locking each
            ' time the method is invoked
            If _instance Is Nothing Then
                SyncLock [syncLock]
                    If _instance Is Nothing Then
                        _instance = New LoadBalancer()
                    End If
                End SyncLock
            End If

            Return _instance

        End Function

        ' Simple, but effective random load balancer
        Public ReadOnly Property Server() As String
            Get
                Dim r As Integer = _random.[Next](_servers.Count)
                Return _servers(r).ToString()
            End Get
        End Property

    End Class

End Namespace